import {
    resolveAndComposeImportMap,
    resolveImportMap,
    resolveIfNotPlainOrUrl,
} from "../dist/s.js";
import assert from "assert";

// function resolveImportMap (importMap, resolvedOrPlain, parentUrl);

function doResolveImportMap(id, parentUrl, importMap) {
    return resolveImportMap(
        importMap,
        resolveIfNotPlainOrUrl(id, parentUrl) || id,
        parentUrl
    );
}

describe("Import Maps", function () {
    const firstImportMap = { imports: {}, scopes: {} };
    resolveAndComposeImportMap(
        {
            imports: {
                "./asdf": "./asdf-asdf",
                t: "./src/t",
                "t/": "./src/t/",
                r: "./src/r",
            },
            scopes: {
                "/": {
                    "y/": "./src/y/",
                },
            },
        },
        "https://sample.com/",
        firstImportMap
    );

    const baseImportMap = {
        imports: Object.assign({}, firstImportMap.imports),
        scopes: {
            "/": Object.assign(
                {},
                firstImportMap.scopes["https://sample.com/"]
            ),
        },
    };
    resolveAndComposeImportMap(
        {
            imports: {
                x: "/y",
                "x/": "/src/x/",
                p: "t",
                "p/": "t/",
                m: "./m/index.js",
                "m/": "./m/",
                "https://site.com/x": "/x.js",
                r: "r",
                g: "./g",
            },
            scopes: {
                "/": firstImportMap.scopes["https://sample.com/"],
                "/scope/": {
                    "x/": "y/",
                },
            },
        },
        "https://sample.com/src/",
        baseImportMap
    );

    it("Throws for unknown", function () {
        try {
            doResolveImportMap("z", "https://site.com", baseImportMap);
        } catch (e) {
            assert.equal(
                e.message,
                'Unable to resolve bare specifier "z" from https://site.com'
            );
        }
    });

    it("Can map URLs", function () {
        assert.equal(
            doResolveImportMap("/x", "https://site.com/", baseImportMap),
            "https://sample.com/x.js"
        );
    });

    it("Can map relative URLs", function () {
        assert.equal(
            doResolveImportMap("/asdf", "https://sample.com/", baseImportMap),
            "https://sample.com/asdf-asdf"
        );
    });

    it("Resolves packages with main sugar", function () {
        assert.equal(
            doResolveImportMap("x", "https://site.com", baseImportMap),
            "https://sample.com/y"
        );
    });

    it("Resolves subpaths with main sugar", function () {
        assert.equal(
            doResolveImportMap("x/z", "https://site.com", baseImportMap),
            "https://sample.com/src/x/z"
        );
    });

    it("Resolves packages with a path", function () {
        assert.equal(
            doResolveImportMap("p/r", "https://site.com", baseImportMap),
            "https://sample.com/src/t/r"
        );
    });

    it("Throws resolving a package with no main", function () {
        try {
            doResolveImportMap("p", "https://site.com", baseImportMap);
        } catch (e) {
            assert.equal(e.message, "Package p has no main");
        }
    });

    it("Resolves packages with a main", function () {
        assert.equal(
            doResolveImportMap("m", "https://site.com", baseImportMap),
            "https://sample.com/src/m/index.js"
        );
    });

    it("Resolves package subpaths for a main", function () {
        assert.equal(
            doResolveImportMap("m/s", "https://site.com", baseImportMap),
            "https://sample.com/src/m/s"
        );
    });

    it("Resolves scoped packages", function () {
        assert.equal(
            doResolveImportMap(
                "x",
                "https://sample.com/scope/y",
                baseImportMap
            ),
            "https://sample.com/y"
        );
    });

    it("Resolves scoped package subpaths", function () {
        assert.equal(
            doResolveImportMap(
                "x/sub",
                "https://sample.com/scope/y",
                baseImportMap
            ),
            "https://sample.com/src/y/sub"
        );
    });

    it("Overrides package resolution when two import maps define the same module", function () {
        const newMap = {
            imports: Object.assign({}, baseImportMap.imports),
            scopes: {},
        };
        resolveAndComposeImportMap(
            {
                imports: {
                    r: "./overridden-r",
                },
            },
            "https://sample.com/src/",
            newMap
        );
        assert.equal(
            doResolveImportMap("r", "https://site.com", newMap),
            "https://sample.com/src/overridden-r"
        );
    });

    it("Adds to an existing import map when there are two import maps", function () {
        const newMap = {
            imports: Object.assign({}, baseImportMap.imports),
            scopes: {},
        };
        resolveAndComposeImportMap(
            {
                imports: {
                    g: "g",
                },
            },
            "https://sample.com/src/",
            newMap
        );
        assert.equal(
            doResolveImportMap("g", "https://site.com", newMap),
            "https://sample.com/src/g"
        );
    });

    it("Supports scopes as exact ids", function () {
        const newMap = {
            imports: Object.assign({}, baseImportMap.imports),
            scopes: baseImportMap.scopes,
        };
        resolveAndComposeImportMap(
            {
                scopes: {
                    "/scope": {
                        "x/": "./z/",
                    },
                },
            },
            "https://sample.com/src/",
            newMap
        );
        assert.equal(
            doResolveImportMap("x/file.js", "https://sample.com/scope", newMap),
            "https://sample.com/src/z/file.js"
        );
        assert.equal(
            doResolveImportMap(
                "x/file.js",
                "https://sample.com/scope/",
                newMap
            ),
            "https://sample.com/src/y/file.js"
        );
    });

    it("Overrides an import map scope when two import maps define the same scope", function () {
        const newMap = {
            imports: Object.assign({}, baseImportMap.imports),
            scopes: {},
        };
        resolveAndComposeImportMap(
            {
                scopes: {
                    "/scope/": {
                        "x/": "./z/",
                    },
                },
            },
            "https://sample.com/src/",
            newMap
        );
        assert.equal(
            doResolveImportMap(
                "x/file.js",
                "https://sample.com/scope/something",
                newMap
            ),
            "https://sample.com/src/z/file.js"
        );
    });

    it("Adds an import map scope when two import maps are merged", function () {
        const newMap = {
            imports: Object.assign({}, baseImportMap.imports),
            scopes: {},
        };
        resolveAndComposeImportMap(
            {
                scopes: {
                    "/other-scope/": {
                        f: "/other-scope-path/f.js",
                    },
                },
            },
            "https://sample.com/src/",
            newMap
        );
        assert.equal(
            doResolveImportMap(
                "f",
                "https://sample.com/other-scope/something",
                newMap
            ),
            "https://sample.com/other-scope-path/f.js"
        );
    });
});
